<?php

namespace App\Task;

use App\Model\Project;
use App\Model\UserModelExpert;
use App\Utils\Enum\RedbillEnum;
use Hyperf\Coroutine\WaitGroup;
use Hyperf\Redis\Redis;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function Hyperf\Coroutine\co;

class CalcRedbillRankingTask extends AbstractBaseTask
{
    protected Redis $redis;
    protected int $recentStartTime = 0;

    public function execute(): void
    {
        $this->init();
        $curId = 0;
        $wg = new WaitGroup();
        $startTime = time();
        $this->recentStartTime = strtotime(date('Y-m-d 00:00:00', strtotime('-7 days')));
        while (true) {
            $rs = UserModelExpert::query()->where('id', '>', $curId)->orderBy('id')->limit(100)->get(['id', 'userid', 'fans_count'])->toArray();
            if (empty($rs)) {
                break;
            }
            $splitRecords = array_chunk($rs, 20);
            $wg->add(count($splitRecords));
            foreach ($splitRecords as $data) {
                co(function () use ($data, $wg) {
                    foreach ($data as $r) {
                        $ret = $this->calcExpertRanking($r);
                    }
                    $wg->done();
                });
            }
            $wg->wait();
            $curId = array_pop($rs)['id'];
        }
        //所在位次
        $rankFields = ['fans_count', 'recent_red_num', 'recent_win_rate', 'recent_payback_rate'];
        foreach ($rankFields as $field) {
            $rankList = UserModelExpert::query()->orderBy($field,'desc')->limit(999)->get(['id']);
            $ids = [];
            foreach ($rankList as $i => $user) {
                UserModelExpert::query()->where('id', $user['id'])->update([$field.'_rank' => $i + 1]);
                $ids[] = $user['id'];
            }
            UserModelExpert::query()->whereNotIn('id', $ids)->update([$field.'_rank' => 1000]);
        }
        $this->logger->info(sprintf('专家数据计算完成 耗时:%d秒', time() - $startTime));
    }

    /**
     * 计算用户排名信息
     * @param $expertInfo
     * @return true|string
     */
    private function calcExpertRanking($expertInfo): true|string
    {
        //获取最近7天的方案数据
        $recentProjects = Project::query()->where([['userid', '=', $expertInfo['userid']], ['add_time', '>=', $this->recentStartTime], ['status', '=', RedbillEnum::STATUS_RESULT_PUBLISH]])->orderBy('id', 'desc')->get(['id', 'match_odds', 'status', 'hit_status', 'play_type_code', 'suggest_result_code', 'real_result_code'])->toArray();
        $update = [
            'recent_win_rate' => 0,
            'recent_payback_rate' => 0,
            'recent_red_num' => 0,
            'day_win' => 0,
            'day_walk' => 0,
            'day_lose' => 0,
            'count_win' => 0,
            'count_walk' => 0,
            'count_lose' => 0
        ];
        if (!empty($recentProjects)) {
            $update['recent_win_rate'] = $this->getHitRate($recentProjects);
            $update['recent_payback_rate'] = $this->getBackRate($recentProjects);
            $update['recent_red_num'] = $this->getContinueRed($recentProjects);
            foreach ($recentProjects as $i => $p) {
                if ($i < 10) {
                    if ($p['hit_status'] == 1) {
                        $update['count_win']++;
                    } elseif ($p['hit_status'] == 2) {
                        $update['count_lose']++;
                    } elseif ($p['hit_status'] == 3) {
                        $update['count_walk']++;
                    }
                }
                if ($p['hit_status'] == 1) {
                    $update['day_win']++;
                } elseif ($p['hit_status'] == 2) {
                    $update['day_lose']++;
                } elseif ($p['hit_status'] == 3) {
                    $update['day_walk']++;
                }
            }
        }
        UserModelExpert::query()->where('userid', $expertInfo['userid'])->update($update);
        try {
            $this->redis->zAdd('expert:rank:fans', $expertInfo['fans_count'], $expertInfo['userid']);
            $this->redis->zAdd('expert:rank:redNum', $update['recent_red_num'], $expertInfo['userid']);
            $this->redis->zAdd('expert:rank:winRate', $update['recent_win_rate'], $expertInfo['userid']);
            $this->redis->zAdd('expert:rank:payBack', $update['recent_payback_rate'], $expertInfo['userid']);
            return true;
        } catch (\RedisException $e) {
            return $e->getMessage();
        }
    }

    /**
     * 获取回报率
     * @param $recentProjects
     * @return float
     */
    private function getBackRate($recentProjects): float
    {
        $odds = 0;
        $hitNum = 0;
        foreach ($recentProjects as $p) {
            if ($p['hit_status'] == 1) {
                $odd = $this->getHitOdds($p['match_odds'], $p['play_type_code'], $p['real_result_code']);
                $hitNum++;
                $odds += $odd;
            }
        }
        return round($odds * 100 / $hitNum, 2);
    }

    /**
     * 获取赔率
     * @param $odds
     * @param $playTypeCode
     * @param $realResultCode
     * @return mixed
     */
    private function getHitOdds($odds, $playTypeCode, $realResultCode): mixed
    {
        $odds = new_json_decode($odds);
        $realResultCode = new_json_decode($realResultCode);
        $allSuggestItems = RedbillEnum::getSuggestItemList($playTypeCode);
        $allSuggestItems = array_combine(array_column($allSuggestItems, 'code'), $allSuggestItems);

        $resultCode = $realResultCode[$playTypeCode];
        if (count($allSuggestItems) == 2) {
            if (in_array($resultCode, [RedbillEnum::SUGGEST_FB_BWL_HOME_WIN, RedbillEnum::SUGGEST_FB_BS_BIG, RedbillEnum::SUGGEST_BB_WL_HOME_WIN, RedbillEnum::SUGGEST_BB_SWL_HOME_WIN, RedbillEnum::SUGGEST_BB_BS_BIG])) {
                $odd = $odds[0];
            } else {
                $odd = $odds[2];
            }
        } elseif ($resultCode == RedbillEnum::SUGGEST_FB_WDL_HOME_WIN) {
            $odd = $odds[0];
        } elseif ($resultCode == RedbillEnum::SUGGEST_FB_WDL_DRAW) {
            $odd = $odds[1];
        } else {
            $odd = $odds[2];
        }
        return $odd;
    }

    /**
     * 获取7日内近10场胜率
     * @param $recentProjects
     * @return int
     */
    private function getHitRate($recentProjects): int
    {
        array_splice($recentProjects, 10);
        $hitNum = 0;
        if (count($recentProjects) == 10) {
            foreach ($recentProjects as $p) {
                if ($p['hit_status'] == 1) {
                    $hitNum++;
                }
            }
        }
        return $hitNum * 10;
    }

    /**
     * 获取7日内连红数
     * @param $recentProjects
     * @return int
     */
    private function getContinueRed($recentProjects): int
    {
        $redNum = 0;
        foreach ($recentProjects as $p) {
            if ($p['hit_status'] == 1) {
                $redNum++;
            } else {
                break;
            }
        }
        return $redNum;
    }

    /**
     * 初始化
     * @return void
     */
    private function init(): void
    {
        try {
            $this->redis = $this->container->get(Redis::class);
        } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
            $this->logger->error('redis连接获取失败');
            return;
        }
    }

}